const second = 1000;
const minute = 60 * second;

const fqueueElem = document.querySelector('.f-queue');
const tqueueElem = document.querySelector('.t-queue');
const queueNumberElem = document.querySelector('.queue-number');
const pileNumberElem = document.querySelector('.pile-number');

const radioElems = document.getElementsByName('chargemode');
const quantityElems = document.querySelectorAll('#charge-quantity');
const totalQuantityElems = document.querySelectorAll('#total-quantity');

const requestForms = document.querySelectorAll('.needs-validation#get-queue-form');
const changeForms = document.querySelectorAll('.needs-validation#change-request-form');

const cancelBtns = document.querySelectorAll('#cancel-charge');
const stopBtn = document.querySelector('#stop-charge');
const changeReqBtn = document.querySelector('#change-request');
const startBtn = document.querySelector('#start-charge');

const readyTimerShown = document.querySelector('.readytimer');
const chargeTimerShown = document.querySelector('.chargetimer');

const queueModal = new bootstrap.Modal(document.getElementById('queueModal'));
const changeModal = new bootstrap.Modal(document.getElementById('changeModal'));

const allStateElems = document.querySelectorAll('.state');
const initStateElems = document.querySelectorAll('.initstate');
const waitStateElems = document.querySelectorAll('.waitstate');
const chargeWaitStateElems = document.querySelectorAll('.cwaitstate');
const readyStateElems = document.querySelectorAll('.readystate');
const chargeStateElems = document.querySelectorAll('.chargestate');


let updateQueueTimer, readyTimer, chargeTimer;
let remainTime;
let currentState, queueNum, pileNum, fNum, tNum, chargeQuantity, chargeMode, totalQuantity;

// if charge mode has been changed, return true
function checkForm(index) {
    let mode;
    for (let i = index * 2; i < index * 2 + 2; i++) {
        if (radioElems[i].checked) {
            mode = radioElems[i].value;
            break;
        }
    }
    chargeQuantity = quantityElems[index].value;

    if (mode !== chargeMode) {
        chargeMode = mode;
        return true;
    }
    return false;
}

// update form state
function updateForm() {
    if (totalQuantity !== undefined && totalQuantity !== 0) {
        totalQuantityElems.forEach(totalq => { totalq.value = totalQuantity; });
        setMax(totalQuantityElems[0]);
    }

    for (let i = 0; i < radioElems.length; i++) {
        if (radioElems[i].value === chargeMode) {
            radioElems[i].checked = true;
        } else {
            radioElems[i].checked = false;
        }
    }
    for (let i = 0; i < quantityElems.length; i++) {
        if (chargeQuantity !== undefined && chargeQuantity !== quantityElems[i].value) {
            quantityElems[i].value = chargeQuantity;
        }
    }
}

// call number forms validation
(() => {
    'use strict'

    // Loop over them and prevent submission
    Array.from(requestForms).forEach(form => {
        form.addEventListener('submit', event => {
            event.preventDefault();
            event.stopPropagation();
            if (form.checkValidity()) {
                // get form info and call_number
                totalQuantity = totalQuantityElems[0].value;
                checkForm(0);
                updateForm();

                // send post request
                let options = {
                    method: 'POST',
                    headers: {
                        'Content-type': 'application/json',
                    },
                    body: JSON.stringify({
                        charge_mode: chargeMode,
                        charge_quantity: chargeQuantity,
                        total_quantity: totalQuantity,
                    }),
                };
                fetch(window.location.pathname + '/callnumber', options)
                    .then(response => {
                        if (!response.ok) {
                            throw new Error(`HTTP error: ${response.status}`);
                        }
                        return response.json();
                    })
                    .then(json => {
                        currentState = json.state;
                        queueNum = json.num;
                        pileNum = json.pile_id;
                        fNum = json.F;
                        tNum = json.T;
                        remainTime = json.remaining_time;
                        changeState();
                    })
                    .catch(err => console.error(`Fetch problem: ${err.message}`));

                queueModal.hide();
            }

            form.classList.add('was-validated');
        }, false)

    })
})();

// change charge request forms validation
(() => {
    'use strict'

    // Loop over them and prevent submission
    Array.from(changeForms).forEach(form => {
        form.addEventListener('submit', event => {
            event.preventDefault();
            event.stopPropagation();
            if (form.checkValidity()) {
                // validate and change request

                let modeChanged = checkForm(1);

                let options = {
                    method: 'POST',
                    headers: {
                        'Content-type': 'application/json',
                    },
                    body: JSON.stringify({
                        mode_changed: modeChanged,
                        charge_mode: chargeMode,
                        charge_quantity: chargeQuantity,
                    }),
                };

                fetch(window.location.pathname + '/change', options)
                    .then(response => {
                        if (!response.ok) {
                            throw new Error(`HTTP error: ${response.status}`);
                        }
                        return response.json();
                    })
                    .then(json => {
                        getCurrentState();
                    })
                    .catch(err => console.error(`Fetch problem: ${err.message}`));

                changeModal.hide();
            }

            form.classList.add('was-validated');
        }, false)
    })
})();

function updateQueue() {
    fetch(window.location.pathname + '/queue')
        .then(response => {
            if (!response.ok) {
                throw new Error(`HTTP error: ${response.status}`);
            }

            return response.json();
        })
        .then(json => {
            fNum = json.F;
            tNum = json.T;
        })
        .catch(err => console.error(`Fetch problem: ${err.message}`));
}

// used for init and wait state to get queue
function startQueueTimer() {
    if (!updateQueueTimer) {
        updateQueue();
        fqueueElem.textContent = fNum, tqueueElem.textContent = tNum;
        updateQueueTimer = setInterval(() => {
            updateQueue();
            fqueueElem.textContent = fNum, tqueueElem.textContent = tNum;
            if ((fNum === 0 && chargeMode === 'F') || (tNum === 0 && chargeMode === 'T')) {
                stopQueueTimer();
            }
        }, 30 * second);
    }
}

function stopQueueTimer() {
    clearInterval(updateQueueTimer);
    updateQueueTimer = null;
}

// used for call number
function getQueueNumber() {
    fetch(window.location.pathname + '/getnumber')
        .then(response => {
            if (!response.ok) {
                throw new Error(`HTTP error: ${response.status}`);
            }

            return response.json();
        })
        .then(json => {
            queueNumberElem.textContent = json.num;
            pileNumberElem.textContent = json.pile_id;
            changeState(json.state);
        })
        .catch(err => console.error(`Fetch problem: ${err.message}`));
}

// used for ready state to countdown
function startReadyTimer() {
    if (!readyTimer) {
        readyTimerShown.textContent = remainTime;
        readyTimer = setInterval(() => {
            remainTime--;
            if (remainTime === 0) {
                stopReadyTimer();
            }
            readyTimerShown.textContent = remainTime;
        }, second);
    }
}

function stopReadyTimer() {
    clearInterval(readyTimer);
    readyTimer = null;
}

function getChargeRemainTime() {
    fetch(window.location.pathname + '/remaintime')
        .then(response => {
            if (!response.ok) {
                throw new Error(`HTTP error: ${response.status}`);
            }

            return response.json();
        })
        .then(json => {
            remainTime = json.remaining_time;
        })
        .catch(err => console.error(`Fetch problem: ${err.message}`));
}

// used for charge state to countdown
function startChargeTimer() {
    if (!chargeTimer) {
        chargeTimerShown.textContent = (remainTime / 60).toFixed(2);
        chargeTimer = setInterval(() => {
            getChargeRemainTime();
            chargeTimerShown.textContent = (remainTime / 60).toFixed(2);
            if (remainTime === 0) {
                stopChargeTimer();
            }
        }, 0.5 * minute);
    }
}

function stopChargeTimer() {
    clearInterval(chargeTimer);
    chargeTimer = null;
}

// used for ready state to start charge
function startCharge() {
    fetch(window.location.pathname + '/confirm')
        .then(response => {
            if (!response.ok) {
                throw new Error(`HTTP error: ${response.status}`);
            }
            return response.json();
        })
        .then(json => {
            if (json.success) {
                getCurrentState();
            } else {
                //TODO: notice stop charge error
                throw new Error(`Start charge didn't get correct response`);
            }
        })
        .catch(err => console.error(`startCharge Fetch problem: ${err.message}`));
}

// used for wait state to cancel charge
function cancelCharge() {
    fetch(window.location.pathname + '/cancel')
        .then(response => {
            if (!response.ok) {
                throw new Error(`HTTP error: ${response.status}`);
            }
            getCurrentState();
        })
        .catch(err => console.error(`cancelCharge Fetch problem: ${err.message}`));
}

// used for charge state to stop charge
function stopCharge() {
    fetch(window.location.pathname + '/stopcharge')
        .then(response => {
            if (!response.ok) {
                throw new Error(`HTTP error: ${response.status}`);
            }
            return response.json();
        })
        .then(json => {
            if (json.success) {
                getCurrentState();
            } else {
                throw new Error(`Stop charge didn't get correct response`);
            }
        })
        .catch(err => console.error(`stopCharge Fetch problem: ${err.message}`));
}

// start charge button event
startBtn.addEventListener('click', startCharge);

// cancel charge button event
cancelBtns.forEach(cancelBtn => {
    cancelBtn.addEventListener('click', cancelCharge);
})

// stop charge button event
stopBtn.addEventListener('click', () => {
    stopCharge();
    Swal.fire({
        icon: 'success',
        title: '充电结束!',
        timer: 1500,
    });
});

// state 0 & 4: init or charge completed
function initState() {
    initStateElems.forEach(elem => { elem.classList.remove('d-none'); });
    chargeMode = undefined, chargeQuantity = undefined, totalQuantity = undefined;
    fqueueElem.textContent = fNum;
    tqueueElem.textContent = tNum;
    updateForm();

    startQueueTimer();
}

// state 1: charging
function chargeState() {
    chargeStateElems.forEach(elem => { elem.classList.remove('d-none'); });
    pileNumberElem.textContent = pileNum;
    startChargeTimer();
}

// state 2: wait for charging in the charging area
function waitChargeState() {
    chargeWaitStateElems.forEach(elem => { elem.classList.remove('d-none'); });
    changeReqBtn.disabled = true;
    pileNumberElem.textContent = pileNum;
}

// state 3: wait for charging in the waiting area
function waitState() {
    waitStateElems.forEach(elem => { elem.classList.remove('d-none'); });
    changeReqBtn.disabled = false;
    fqueueElem.textContent = fNum;
    tqueueElem.textContent = tNum;

    updateForm();
    startQueueTimer();
}

// state 5: ready to charge
function readyState() {
    Swal.fire({
        icon: 'warning',
        title: '请在倒计时内到达指定充电桩并进行确认!',
        timer: 2000,
    });
    readyStateElems.forEach(elem => { elem.classList.remove('d-none'); });
    pileNumberElem.textContent = pileNum;
    startReadyTimer();
}

function changeState() {
    stopQueueTimer();
    stopReadyTimer();
    stopChargeTimer();
    allStateElems.forEach(elem => { elem.classList.add('d-none'); });
    queueNumberElem.textContent = queueNum !== 0 ? queueNum : '暂无';

    switch (currentState) {
        // login and charge complete
        case 0:
        case 4:
            initState();
            break;
        // charging
        case 1:
            chargeState();
            break;
        case 2:
            waitChargeState();
            break;
        // waiting at wait area
        case 3:
            waitState();
            break;
        // ready to charge
        case 5:
            readyState();
            break;
        default:
            break;
    }
}


// init, get current state and render page
function getCurrentState() {
    fetch(window.location.pathname + '/state')
        .then(response => {
            if (!response.ok) {
                throw new Error(`HTTP error: ${response.status}`);
            }

            return response.json();
        })
        .then(json => {
            currentState = json.state;
            queueNum = json.num;
            pileNum = json.pile_id;
            fNum = json.F;
            tNum = json.T;
            totalQuantity = json.total_quantity;
            chargeQuantity = json.charge_quantity;
            chargeMode = json.chargeMode;
            remainTime = json.remaining_time;
            changeState();
        })
        .catch(err => console.error(`Fetch problem: ${err.message}`));
}

getCurrentState();
